// udpconn project udpconn.go
package udpconn

import (
	"errors"
	"net"
	"strings"
	"sync"
)

type Pool struct {
	Address        string
	Bak            string
	CurrentAddress string
	LocalAddress   string //本机地址
	maxNum         int
	pool           chan *net.UDPConn
	lock           *sync.Mutex
	info           string
}

func New(host string, localHost string, num int) (p *Pool, err error) {
	if host == "" || localHost == "" {
		err = errors.New("The address of server is empty.")
		return
	}
	p = new(Pool)
	ss := strings.Split(host, ";")
	p.Address = ss[0]
	p.CurrentAddress = p.Address
	if len(ss) > 1 {
		p.Bak = ss[1]
	} else {
		p.Bak = p.Address
	}
	p.LocalAddress = localHost
	p.SetSize(num)
	err = p.Initialize()
	return
}

func (this *Pool) GetInfo() string {
	return this.info
}

func (this *Pool) Initialize() (err error) {
	this.lock = new(sync.Mutex)
	this.pool = make(chan *net.UDPConn, this.GetSize())
	return
}

func (this *Pool) SetSize(num int) {
	if num < 1 {
		num = 1
	}
	this.maxNum = num
}

//设置最大数量
func (this *Pool) GetSize() int {
	if this.maxNum < 1 {
		this.maxNum = 1
	}
	return this.maxNum
}

func (this *Pool) SetAddress(address string) {
	this.Address = address
}

func (this *Pool) GetAddress() string {
	return this.Address
}

func (this *Pool) Lock() *sync.Mutex {
	return this.lock
}

//获取长度
func (this *Pool) Len() int {
	return len(this.pool)
}

//获取连接
func (this *Pool) Get() (conn *net.UDPConn, err error) {
	this.lock.Lock()
	defer this.lock.Unlock()
	if len(this.pool) == 0 {
		s := "Connect to " + this.CurrentAddress
		conn, err = CreateUdpConnect(this.CurrentAddress, this.LocalAddress)
		if this.CurrentAddress == this.Address {
			this.CurrentAddress = this.Bak
		} else {
			this.CurrentAddress = this.Address
		}
		if err == nil {
			this.info = s + " OK."
		}
		return
	}
	conn = <-this.pool
	return
}

//释放连接
func (this *Pool) Put(conn *net.UDPConn) (err error) {
	this.lock.Lock()
	defer this.lock.Unlock()

	if this.info != "" {
		this.info = ""
	}

	if len(this.pool) >= this.maxNum {
		conn.Close()
		err = errors.New("Pool is full")
		return
	}
	this.pool <- conn
	return
}

//清空
func (this *Pool) Clear() {
	this.lock.Lock()
	defer this.lock.Unlock()

	close(this.pool)
	for conn := range this.pool {
		conn.Close()
	}
	this.pool = nil
}
func CreateUdpConnect(address string, laddress string) (conn *net.UDPConn, err error) {
	var addr, laddr *net.UDPAddr
	addr, err = net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, err
	}
	if laddress != "" {
		laddr, err = net.ResolveUDPAddr("udp", laddress)
	} else {
		laddr = nil
	}
	conn, err = net.DialUDP("udp4", laddr, addr)
	if err != nil {
		return nil, err
	}
	return conn, err
}
